﻿#include "TcpServer.h"
#include "as_acceptor.h"
#include "TcpConnection.h"
#include "epoll_watch.h"
#include "byte_buffer.h"
#include "log.h"
#include <iostream>


as::net::TcpServer::TcpServer(std::shared_ptr<EpollWatch> &ep, const Address &listenAddr, 
	const std::string_view &name)
	:epoll_watch_(ep), svr_name_(name)
{
	acceptor_ = std::make_unique<Acceptor>(ep, listenAddr);
	acceptor_->SetNewConnCallback(std::bind(&TcpServer::HandleNewConn, this, std::placeholders::_1, 
		std::placeholders::_2));
}

as::net::TcpServer::~TcpServer()
{
	for (auto &it : conns_)
	{
		TcpConnectionPtr conn(it.second);
		it.second.reset();
		conn->GetWatch()->IsRunInLoop([&conn]() {conn->DestoryConn(); });
	}
}

void as::net::TcpServer::Start()
{
	is_stop_ = false;
	acceptor_->bind();
	acceptor_->listen();
}

void as::net::TcpServer::Stop()
{
	acceptor_->close();
	is_stop_ = true;
}

int as::net::TcpServer::GetConnCount()
{
	return (int)conns_.size();
}

as::net::TcpConnectionPtr as::net::TcpServer::GetConn(int connId)
{
	auto it = conns_.find(connId);
	if (it != conns_.end())
		return it->second;
	return nullptr;
}

const char * as::net::TcpServer::GetName()
{
	return this->svr_name_.c_str();
}

as::co::Awaitable<as::co::as_promise> as::net::TcpServer::HandleCoRead(TcpConnectionPtr conn)
{
	while (is_stop_ == false)
	{
		int nread = co_await conn->co_read();
		LOG_DEBUG << "co read n:" << nread << " connid:" << conn->GetConnId();
		if (conn->iSConned() == false)
		{
			break;
		}
	}
}

void as::net::TcpServer::HandleNewConn(socket_fd fd, Address &peek)
{
	using namespace std::placeholders;
	if (epoll_watch_.expired() == false) [[likely]]
	{
		Address localAddr = Socket::get_local_addr(fd);
		Address peerAddr = Socket::get_peer_addr(fd);
		auto ep = epoll_watch_.lock();
		TcpConnectionPtr newConn = std::make_shared<TcpConnection>(ep, fd, svr_name_.c_str(), 
			localAddr, peerAddr);
		int connId = ep->GenerateConnId();
		newConn->SetConnId(connId);
		newConn->SetMsgCallback(std::bind(&TcpServer::HandleMsg, this, _1, _2, _3));
		newConn->SetConnectCallback(conn_change_cb_);
		newConn->SetCloseCallback(std::bind(&TcpServer::HandleCloseConn, this, _1));
		HandleCoRead(newConn);

		conns_[connId] = newConn;
		LOG_DEBUG << "tcp server new fd:" << fd << " connid:" << connId << " local addr:" 
			<< localAddr.GetAddr().c_str() << " peerAddr:" << peerAddr.GetAddr().c_str();

		newConn->ConnectFinish();
	}
}

void as::net::TcpServer::HandleCloseConn(const TcpConnectionPtr &conn)
{
	int connId = conn->GetConnId();
	if (close_cb_)
	{
		close_cb_(conn);
	}

	auto it = conns_.find(connId);
	if (it != conns_.end())
	{
		conns_.erase(it);
		LOG_DEBUG << "remove conn:" << connId;

		auto ep = conn->GetWatch();
		if (ep)
		{
			ep->AddTimer(std::bind(&TcpConnection::DestoryConn, conn));
		}
		else
		{
			LOG_WARNING << "not find ep, conn:" << connId;
		}
	}
	else
	{
		LOG_ERROR << "not find conn:" << connId;
	}
}

void as::net::TcpServer::HandleMsg(TcpConnectionPtr &conn, ByteBufferPtr &buf, int size)
{
	if (msg_cb_)
	{
		msg_cb_(conn, buf, size);
	}
	buf->AddReadPos(size);
}

void as::net::TcpServer::SendMsg(int connId, const char *buf, int size)
{
	auto it = conns_.find(connId);
	if (it == conns_.end())
		return;
	SendMsgByConn(it->second, buf, size);
}

void as::net::TcpServer::SendMsgByConn(TcpConnectionPtr &conn, const char *buf, int size)
{
	conn->SendMsg(buf, size);
}

void as::net::TcpServer::SendMsg(int connId, ByteBuffer &buf)
{
	auto it = conns_.find(connId);
	if (it == conns_.end())
		return;

	SendMsgByConn(it->second, (const char *)buf.GetReadPtr(), buf.ReadableBytes());
}

void as::net::TcpServer::SendMsg(int connId, ByteBufferPtr &buf)
{
	auto it = conns_.find(connId);
	if (it == conns_.end())
		return;

	SendMsgByConn(it->second, (const char *)buf->GetReadPtr(), buf->ReadableBytes());
}

void as::net::TcpServer::SetConnCb(ConnChangeCb fun)
{
	conn_change_cb_ = std::move(fun);
}

void as::net::TcpServer::SetMsgCb(MsgCallback fun)
{
	msg_cb_ = std::move(fun);
}

void as::net::TcpServer::SetCloseCb(CloseCallback fun)
{
	close_cb_ = std::move(fun);
}

